﻿using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using ReagentStore.BLL;
using ReagentStore.ERM;
using ReagentStore.UTL;

namespace ReagentStore.Report
{
	public interface IReport
	{
		string ReportPath { get; }
		DataTable ReportData { get; }
		bool IsLoaded { get; }
		void LoadData();
	}

	internal class ExpirationReport : IReport
	{
		private readonly ExpirationSet _data = new ExpirationSet();

		#region Implementation of IReport

		public string ReportPath
		{
			get { return @"ReagentStore.Report.ExpirationReport.rdlc"; }
		}

		public DataTable ReportData
		{
			get { return _data.Expiration; }
		}

		public bool IsLoaded
		{
			get { return _data.Expiration.Rows.Count > 0; }
		}

		public void LoadData()
		{
			using (var db = new ReagentStoreContainer(new ConnectionStringBuilder().EntityConnectionString()))
			{
				DateTime now = DateTime.UtcNow;
				var list = new List<Product>();
				List<Product> products = db.Products.Include("Reagent").OrderBy(p => p.ExpirationDate).ToList();

				products.ForEach(p =>
				                 	{
				                 		if ((p.ExpirationDate - now).Days <= p.Reagent.AlertPeriod)
				                 			list.Add(p);
				                 	});

				foreach (Product product in list)
				{
					ExpirationSet.ExpirationRow row = _data.Expiration.NewExpirationRow();

					row.ProductID = product.ProductID;
					row.ReagentName = product.Reagent.Name;
					row.Manufacturer = product.Manufacturer;
					row.Specification = product.Specification;
					row.Batch = product.Batch;
					row.ExpirationDate = product.ExpirationDate.ToLocalTime();
					row.Quantity = product.Quantity();
					row.Unit = product.Reagent.Unit;

					_data.Expiration.AddExpirationRow(row);
				}

				_data.Expiration.AcceptChanges();
			}
		}

		#endregion
	}

	internal class PurchaseReport : IReport
	{
		private readonly PurchaseSet _data = new PurchaseSet();

		#region Implementation of IReport

		public string ReportPath
		{
			get { return @"ReagentStore.Report.PurchaseReport.rdlc"; }
		}

		public DataTable ReportData
		{
			get { return _data.Purchase; }
		}

		public bool IsLoaded
		{
			get { return _data.Purchase.Rows.Count > 0; }
		}

		public void LoadData()
		{
			IList<Reagent> list = new ReagentBLL().GetPurchaseList();
			foreach (Reagent reagent in list)
			{
				PurchaseSet.PurchaseRow row = _data.Purchase.NewPurchaseRow();

				row.ReagentID = reagent.ReagentID;
				row.ReagentName = reagent.Name;
				row.Unit = reagent.Unit;
				row.StandingCount = reagent.StandingCount;
				row.AlertCount = reagent.AlertCount;
				row.Quantity = reagent.Quantity();

				_data.Purchase.AddPurchaseRow(row);
			}

			_data.AcceptChanges();
		}

		#endregion
	}

	internal class RecordReport : IReport
	{
		private readonly RecordSet _data = new RecordSet();

		private DateTime _dateBegin;
		private DateTime _dateEnd;

		public RecordReport()
		{
			ReagentID = 0;
			_dateBegin = DateTime.Today;
			_dateEnd = DateTime.Today.AddDays(1);
		}

		#region Implementation of IReport

		public int ReagentID { get; set; }

		public DateTime DateBegin
		{
			get { return _dateBegin; }
			set
			{
				if (value > _dateEnd)
					throw new ArgumentException();

				_dateBegin = value;
			}
		}

		public DateTime DateEnd
		{
			get { return _dateEnd; }
			set
			{
				if (value < _dateBegin)
					throw new ArgumentException();

				_dateEnd = value;
			}
		}

		public string ReportPath
		{
			get { return @"ReagentStore.Report.RecordReport.rdlc"; }
		}

		public DataTable ReportData
		{
			get { return _data.Record; }
		}

		public bool IsLoaded
		{
			get { return _data.Record.Rows.Count > 0; }
		}

		public void LoadData()
		{
			if (ReagentID > 0)
				LoadDataWithReagent();
			else
				LoadDataWithoutReagent();
		}

		#endregion

		private void LoadDataWithReagent()
		{
			using (var db = new ReagentStoreContainer(new ConnectionStringBuilder().EntityConnectionString()))
			{
				IOrderedQueryable<Record> list = db.Records.Include("Product")
					.Where(r => r.Product.ReagentID == ReagentID)
					.Where(r => r.Date >= DateBegin && r.Date <= DateEnd)
					.OrderBy(r => r.Date);

				_data.Record.Clear();

				foreach (Record record in list)
				{
					RecordSet.RecordRow row = _data.Record.NewRecordRow();

					row.RecordID = record.RecordID;
					row.RecordDate = record.Date;
					row.Action = record.Caption();
					row.ReagentName = record.Product.Reagent.Name;
					row.Count = record.Count;
					row.Unit = record.Product.Reagent.Unit;
					row.Manufacturer = record.Product.Manufacturer;
					row.Specification = record.Product.Specification;
					row.Batch = record.Product.Batch;
					row.ExpirationDate = record.Product.ExpirationDate.ToLocalTime();

					if (record is IncomingRecord)
						row.Memo = ((IncomingRecord) record).Bill;
					else if (record is OutgoingRecord)
						row.Memo = ((OutgoingRecord) record).Recipient;
					else if (record is WearRecord)
						row.Memo = ((WearRecord) record).Reason;

					_data.Record.AddRecordRow(row);
				}

				_data.AcceptChanges();
			}
		}

		private void LoadDataWithoutReagent()
		{
			using (var db = new ReagentStoreContainer(new ConnectionStringBuilder().EntityConnectionString()))
			{
				IOrderedQueryable<Record> list = db.Records.Include("Product")
					.Where(r => r.Date >= DateBegin && r.Date <= DateEnd)
					.OrderBy(r => r.Date);

				_data.Record.Clear();

				foreach (Record record in list)
				{
					RecordSet.RecordRow row = _data.Record.NewRecordRow();

					row.RecordID = record.RecordID;
					row.RecordDate = record.Date;
					row.Action = record.Caption();
					row.ReagentName = record.Product.Reagent.Name;
					row.Count = record.Count;
					row.Unit = record.Product.Reagent.Unit;
					row.Manufacturer = record.Product.Manufacturer;
					row.Specification = record.Product.Specification;
					row.Batch = record.Product.Batch;
					row.ExpirationDate = record.Product.ExpirationDate.ToLocalTime();

					if (record is IncomingRecord)
						row.Memo = ((IncomingRecord) record).Bill;
					else if (record is OutgoingRecord)
						row.Memo = ((OutgoingRecord) record).Recipient;
					else if (record is WearRecord)
						row.Memo = ((WearRecord) record).Reason;

					_data.Record.AddRecordRow(row);
				}

				_data.AcceptChanges();
			}
		}
	}

	internal class StockReport : IReport
	{
		private readonly StockSet _data = new StockSet();

		#region Implementation of IReport

		public string ReportPath
		{
			get { return @"ReagentStore.Report.StockReport.rdlc"; }
		}

		public DataTable ReportData
		{
			get { return _data.Stock; }
		}

		public bool IsLoaded
		{
			get { return _data.Stock.Rows.Count > 0; }
		}

		public void LoadData()
		{
			using (var db = new ReagentStoreContainer(new ConnectionStringBuilder().EntityConnectionString()))
			{
				foreach (Reagent reagent in db.Reagents)
					foreach (Product product in reagent.Products)
					{
						StockSet.StockRow row = _data.Stock.NewStockRow();

						row.ProductID = product.ProductID;
						row.ReagentName = reagent.Name;
						row.Manufacturer = product.Manufacturer;
						row.Specification = product.Specification;
						row.Batch = product.Batch;
						row.ExpirationDate = product.ExpirationDate.ToLocalTime();
						row.Quantity = product.Quantity();
						row.Unit = reagent.Unit;

						_data.Stock.AddStockRow(row);
					}

				_data.AcceptChanges();
			}
		}

		#endregion
	}
}